﻿using Zzw;
using FictionsDownload.Model;
using FictionsDownload.Static;
using FictionsDownload.UI;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Threading.Tasks.Schedulers;
using System.Windows.Forms;

namespace FictionsDownload.BLL
{
    public class Thread_BookDownload
    {
        #region 单例初始化
        private static Thread_BookDownload Instance;
        private static readonly object lockRoot = new object();
        /// <summary>
        /// 初始化实例
        /// </summary>
        public static Thread_BookDownload GetIntance
        {
            get
            {
                if (Instance == null)
                {
                    lock (lockRoot)
                    {
                        if (Instance == null)
                        {
                            Instance = new Thread_BookDownload();
                        }
                    }
                }
                return Instance;
            }
        }
        private Thread_BookDownload()
        {
        }
        #endregion
        #region 内部变量
        private Thread thread;
        private Directory directory = new Directory();
        private Book book = new Book();
        private Folder folder = new Folder();
        #endregion
        #region 公共属性和事件
        /// <summary>
        /// 下载中的书籍
        /// </summary>
        public BookModel DownloadingBook { get; private set; }
        /// <summary>
        /// 开始下载
        /// </summary>
        public event EventHandler StartDownload;
        /// <summary>
        /// 结束下载
        /// </summary>
        public event EventHandler StopDownload; 
        /// <summary>
        /// 下载某本书的目录前
        /// </summary>
        public event EventHandler ToDirectoryChanging;
        /// <summary>
        /// 下载某本书的目录后
        /// </summary>
        public event EventHandler ToDirectoryChanged;
        /// <summary>
        /// 下载某本书的章节内容前
        /// </summary>
        public event EventHandler DownLoadTextChanging;
        /// <summary>
        /// 下载某本书的章节内容后
        /// </summary>
        public event EventHandler DownLoadTextChanged;
        #endregion

        /// <summary>
        /// 循环下载书籍和章节
        /// </summary>
        /// <param name="pair">KeyValuePair<string, Type> key是下载Id,Type是下载类型</param>
        private void ForeachDownload(object pair)
        {
            KeyValuePair<string, Type> keyValuePair = (KeyValuePair<string, Type>)pair;
            if(keyValuePair.Value == new DirectoryModel().GetType())
            {
                DownloadText(keyValuePair.Key);
            }
            else if (keyValuePair.Value == new FolderModel().GetType())
            {
                if (keyValuePair.Key == FolderModel.BaseNode)
                {
                    //全部书籍下载
                    List<FolderModel> folders = folder.GetList<FolderModel>().OrderBy(m => m.SortIndex).ToList();
                    foreach (FolderModel item in folders)
                    {
                        DownloadFolder(item.Id);
                    }
                }
                else
                {
                    DownloadFolder(keyValuePair.Key);
                }
            }
            else if (keyValuePair.Value == new BookModel().GetType())
            {
                BookModel bookModel = book.Get<BookModel>(b => b.Id == keyValuePair.Key);
                if (bookModel != null)
                {
                    DownloadingBook = bookModel;
                    DownloadBook(bookModel);
                    DownloadText(bookModel);
                }
            }
            StopDownload?.Invoke(this, null);
        }
        /// <summary>
        /// 下载指定文件夹内的所有书籍
        /// </summary>
        /// <param name="folderModel"></param>
        private void DownloadFolder(string folderId)
        {
            List<BookModel> books = book.GetList<BookModel>(m => m.FolderId == folderId);
            foreach (BookModel book in books)
            {
                if (book.State == BookModel.EState.正常更新)
                {
                    DownloadingBook = book;
                    DownloadBook(book);
                    DownloadText(book);
                }
            }
        }
        /// <summary>
        /// 更新书籍目录
        /// </summary>
        /// <param name="bookModel"></param>
        private void DownloadBook(BookModel bookModel)
        {
            ToDirectoryChanging?.Invoke(this, null);
            //更新目录
            var keyValuePairs = book.ToDirectorys(bookModel);
            if (keyValuePairs != null)
            {
                directory.Add(bookModel, keyValuePairs);
                directory.SaveChanges();
            }
            ToDirectoryChanged?.Invoke(this, null);
        }
        #region DownloadText，章节内容下载
        /// <summary>
        /// 更新章节内容
        /// </summary>
        /// <param name="book"></param>
        private void DownloadText(BookModel book)
        {
            DownLoadTextChanging?.Invoke(this, null);
            var directories = directory.GetList<Model.DirectoryModel>(d => d.BookId == book.Id && d.State == true && (d.Len == null || d.Len == 0));
            
            //并发数2就有4-5章的下载速度，并发数3有5-6章的下载速度，再加并发数量，下载速度无法提升。
            var scheduler = new LimitedConcurrencyLevelTaskScheduler(3);
            //记录一下，单线程下载，一秒钟下载2-3章。
            //异步下载一秒钟5-6章，而且程序很容易卡滞。
            Task<bool>[] tasks = new Task<bool>[directories.Count];
            for(int i = 0; i < directories.Count; i++)
            {
                //这一步是必须的，不能将directories[i]直接放到Task中做DownloadText的参数，否者i会每次循环后发生变化，传到DownloadText里面就不是你想要传的那个对象了。
                var dir = directories[i];
                tasks[i] = Task<bool>.Factory.StartNew(() => { return DownloadText(dir, book); }, CancellationToken.None, TaskCreationOptions.None, scheduler);
            }
            Task.WaitAll(tasks);
            DownLoadTextChanged?.Invoke(this, null);
        }

        private static readonly object lockUpdateText = new object();
        /// <summary>
        /// 下载章节内容
        /// </summary>
        /// <param name="directory"></param>
        public bool DownloadText(DirectoryModel directoryModel, BookModel book)
        {
            string html = HttpHelper.DownloadHtml(directoryModel.Url);
            if (html == null)
            {
                return false;
            }
            else
            {
                string bodyHtml = HtmlHelper.HtmlTrim(html, book.TextHead, book.TextTail);
                HtmlToText convert = new HtmlToText();
                string text = convert.Convert(bodyHtml);
                lock (lockUpdateText)
                {
                    Directory directory = new Directory();
                    var updateDir = directory.Find<DirectoryModel>(directoryModel.Id);
                    //同一本书，一边下载，一边删除，就有可能发生这种下载后查找不到的情况
                    if(updateDir != null)
                    {
                        updateDir.Len = text.Length;
                        updateDir.Text = text;
                        updateDir.ActiveTime = DateTime.Now;
                        //更新章节
                        directory.Update(updateDir);
                        directory.SaveChanges();
                    }
                }
                return true;
            }
        }

        /// <summary>
        /// 章节文本下载
        /// </summary>
        /// <param name="directoryId"></param>
        private void DownloadText(string directoryId)
        {
            //查找章节
            var directoryModel = directory.Get<DirectoryModel>(m=>m.Id == directoryId);
            var book = directory.Get<BookModel>(b => b.Id == directoryModel.BookId);
            DownloadingBook = book;
            DownLoadTextChanging?.Invoke(this, null);
            DownloadText(directoryModel,book);
            DownLoadTextChanged?.Invoke(this, null);
        }
        #endregion
        public void Start(string id,Type type)
        {
            if (thread == null||thread.ThreadState == ThreadState.Aborted || thread.ThreadState == ThreadState.Stopped)
            {
                thread = new Thread(new ParameterizedThreadStart(ForeachDownload));
                thread.IsBackground = true;
            }
            else if (thread.ThreadState == ThreadState.Background)
            {
                MessageBox.Show("下载任务在执行，无法重复执行");
                return;
            }
            StartDownload?.Invoke(this, null);
            thread.Start(new KeyValuePair<string,Type>(id, type));
        }
        public void Stop()
        {
            thread.Abort();
            thread.Join();
            StopDownload?.Invoke(this, null);
        }
    }

}
